package com.cy.pj.common.aspect;

import com.cy.pj.common.cache.SimpleCacheKey;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 张琪
 * @date 2020/11/12
 * @describe 为目标业务做cache功能增强
 */
@Aspect
@Component
public class SysCacheAspect {

//    ConcurrentHashMap 线程安全的散列表
    private Map<Object, Object> cache = new ConcurrentHashMap<>();

    /**
     * bean()语句 是一种粗粒度的切入点表达式
     */
//    @Pointcut("bean(*ServiceImpl)")
//    @Pointcut("execution(* com.cy.pj..*.findBrnads(..))")   //细粒度切入点
    @Pointcut("@annotation(com.cy.pj.common.anno.RequiredCache)")
    public void doCache(){}

//    @Pointcut("execution(* com.cy.pj..*.deleteBrandById(..))")
    @Pointcut("@annotation(com.cy.pj.common.anno.ClearCache)") // 使用ClearCache注解的方法, 是切入点方法
//    使用该注解后, 指定了需要执行切入点方法的目标函数
    public void doClearCache(){}

    @Order(1)       //优先级为1
    @Around("doCache()")
    /**
     * 当前应用程序问题
     * 1) key的设计(key后续在项目中一般是基于请求参数动态生成)
     * 2) 切入点的设计(缓存一般应用与查询方法, 但切入点是针对所有的方法)     @annotation来解决问题, 可以让设置任意方法为切入点
     * 3) 脏读问题? (数据库中数据已经删除, 但缓存中还存在 || 减少数据库的访问压力)   @annotation来解决问题, 增删改后清空缓存
     */
    public Object doCacheAround(ProceedingJoinPoint joinPoint) throws Throwable {
        /*
        * 1. 从缓存取数据, 加入缓存中有则直接执行, 否则查询数据库
        * 2. 查询数据库
        * 3. 存储到缓存中*/

        Object[] args =  joinPoint.getArgs();            //获取目标方法执行时传入的实际参数
        System.err.println(args);
        System.err.println(Arrays.toString(args));
        SimpleCacheKey key = new SimpleCacheKey(args);
//        1. 缓存中取
        Object result = cache.get(key);// 暂时将key设计为固定值
        if(result != null) {
            System.err.println("缓存命中");
            return result;
        }
//        2.不存在时查询数据库
        result = joinPoint.proceed();
//        3. 存入缓存中
        cache.put(key, result);
        return result;
    }

    /*可以这么写, 但无效操作太多, 获取到的obj 没有任何用处*/
    /*@Around("doClearCache()")
    public Object doCleaCacheAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = joinPoint.proceed();
        cache.clear();
        System.err.println("清空成功");
        return obj;
    }*/
    @AfterReturning("doClearCache()")  // 在目标方法成功执行后, 执行该方法
    public void doClearCacheAfterReturning(){
        cache.clear();
    }


}
